/*************************************************************************
 * Nuvoton Technology Corporation confidential
 *
 * Copyright (c) 2009 by Nuvoton Technology Corporation
 * All rights reserved
 * 
 * FILENAME
 *     IdeFunctions.c
 *
 * VERSION
 *     1.0
 *
 * DESCRIPTION
 *     This file contains ATAPI disk control functions.
 *
 * HISTORY
 *     2008.06.25		Created
 *
 * REMARK
 *     None
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "drv_api.h"
#else
#include <stdio.h>
#include <string.h>
#endif
#include "Atapi.h"
#include "AtapiReg.h"

_hd_information IDE_HEADER_INFO[2];     // HD header info format (master and slave)
UCHAR gDMAtype;        // 0: DMA   1: UDMA  
volatile BOOL bAtapiMasterIsUsed = FALSE;
volatile BOOL bAtapiSlaveIsUsed = FALSE;

// Interrupt flags
volatile UINT32 _atapi_DMACRWABORT, 		// DMAC READ/WRITE Target Abort Interrupt Flag 
              _atapi_ENDOFSECTOR,           // End of Sectors Transfer Interrupt Flag
              _atapi_DMARQ,     			// DMARQ Interrupt Flag
              _atapi_INTRQ;      			// INTRQ Interrupt Flag 
#ifdef ECOS
cyg_interrupt  atapi_int;
cyg_handle_t   atapi_int_handle;
#endif
//------------------------------------------------------------------------
//  Interrupt service routine for ATAPI interface controller 
//------------------------------------------------------------------------
#ifdef ECOS
static cyg_uint32 atapiISR(cyg_vector_t vector, cyg_addrword_t data)
#else
int atapiISR(void)
#endif
{
    UINT32 uInterruptStatus;
    
    uInterruptStatus = inpw(ATAPI_INTR);

    // DMAC READ/WRITE Target Abort Interrupt Flag
    if(uInterruptStatus & ATAPI_INTR_DTA_IF)
    {
        _atapi_DMACRWABORT = TRUE;

	 // Write one to clear this interrupt status	            
        outpw(ATAPI_INTR, (uInterruptStatus & 0xF) | ATAPI_INTR_DTA_IF); 	            
    }
    
    // End of Sectors Transfer Interrupt Flag
    if(uInterruptStatus & ATAPI_INTR_EOS_IF)
    {
	    _atapi_ENDOFSECTOR = TRUE;
	    
	 // Write one to clear this interrupt status	            
        outpw(ATAPI_INTR, (uInterruptStatus & 0xF) | ATAPI_INTR_EOS_IF); 	    
    }
    
    // DMARQ Interrupt Flag 
    if(uInterruptStatus & ATAPI_INTR_DMARQ_IF)
    {
	    _atapi_DMARQ = TRUE;
	    
	 // Write one to clear this interrupt status	            
        outpw(ATAPI_INTR, (uInterruptStatus & 0xF) | ATAPI_INTR_DMARQ_IF); 	    
    }
    
    // INTRQ Interrupt Flag
    if(uInterruptStatus & ATAPI_INTR_INTRQ_IF)
    {
	    _atapi_INTRQ = TRUE;
	    
	    //sysprintf("INTRQ occurs...\n");
	 // Write one to clear this interrupt status	            
        outpw(ATAPI_INTR, (uInterruptStatus | ATAPI_INTR_INTRQ_IF)); 	    
    }
    
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(IRQ_ATAPI);
	return CYG_ISR_HANDLED;
#else    
    return TRUE;
#endif
}
//------------------------------------------------------------------------
UINT32 RegTimingTable[2][5]=
{   {0x01090103, 0x00090000, 0x00090000, 0x00040000, 0x00040000},
     {0x0313010b, 0x02130100, 0x00190000, 0x00050001, 0x00040000}
};
UINT32 PIOTimingTable[2][5]=
{   {0x01050104, 0x00040001, 0x00040000, 0x00040000, 0x00040000},
     {0x030b010f, 0x02080105, 0x00060100, 0x00050000, 0x00040000}
};
UINT32 DMATimingTable[3]={0x00040d0f, 0x00030402, 0x00020301};
UINT32 UDMATimingTable[5]={0x0006040b, 0x00040309, 0x00020207, 0x00010107, 0x00000006};

#define   UDMA_33M_MODE0_TIMING    0x00020206 
#define   DMA_33M_MODE0_TIMING      0x00020606 
//------------------------------------------------------------------------
//  Set transfer timing control register
//
//  type : 0: Register transfer timing
//            1: PIO transfer timing
//            2: DMA transfer timing
//            3: Ultra DMA transfer timing
//  mode : transfer mode(0-4)
//  clock  : 0: 33 MHz
//             1: 66 MHz
//------------------------------------------------------------------------
int atapiSetTransferTiming(USHORT type, USHORT mode, USHORT clock)
{
	switch(type)
		{
			case 0:   // Set Register transfer timing register
					outpw(ATAPI_REGTTR, RegTimingTable[clock][mode]);
					//sysprintf("%8x\n",RegTimingTable[clock][mode]);
					break;
			case 1:   // Set PIO transfer timing register
					outpw(ATAPI_PIOTTR, PIOTimingTable[clock][mode]);
					//sysprintf("%8x\n",PIOTimingTable[clock][mode]);
					break;
			case 2:   // Set DMA transfer timing register
			        if (clock==0)   // 33 MHz, only support Mode 0
					    outpw(ATAPI_DMATTR, DMA_33M_MODE0_TIMING);
 				    else
					    outpw(ATAPI_DMATTR, DMATimingTable[mode]);
					//sysprintf("%8x\n",DMATimingTable[mode]);   
					break;
			case 3:   // Set UDMA transfer timing register
			        if (clock==0)  
					    outpw(ATAPI_UDMATTR, UDMA_33M_MODE0_TIMING);						  	
				  	else
					    outpw(ATAPI_UDMATTR, UDMATimingTable[mode]);
					//sysprintf("%8x\n",UDMATimingTable[mode]);
					break;									
		}
	return 1;
}

//------------------------------------------------------------------------
// Write	sectors of datas into HD. If success then
//		  return 0, else return 1
//
//  transfer_type :   0 : PIO mode
//                           1 : DMA mode
//                           2 : UDMA mode
//  mode : 0~4
//
//  Input  : UINT32 initial sector number,
//	     UCHAR  count of sectors,
//	     a data pointer of the data	buffer 
//
//  output : 0: Failed
//              1 : Success
//------------------------------------------------------------------------
int atapiSetFeature(UCHAR drive_no, USHORT transfer_type, USHORT mode)
{
    UINT32 mode_value;
    volatile  UINT32 param;	
    	
    if (transfer_type==0)    // PIO mode
	  mode_value=1 << 3;
    else if (transfer_type==1)    // DMA mode	  	
	  mode_value=1 << 5;
    else if (transfer_type==2)    // UDMA mode	  	
	  mode_value=1 << 6;
    else  
    {
       	sysprintf("Not Support this mode !!\n");	 
	 	return 0;
	}
    
	
    mode_value |= mode;
 
    // prepare  parameter 
    if (!atapi_wait_BSY())
    {
    	sysprintf("wait BSY error !!\n");	 
	 	return 0;
	}
	 
    /*if (!atapi_wait_DRQ(0))
    {
    	sysprintf("wait DQR error !!\n");	 
        return 0;
    }*/

    ide_outp(ATAPI_ATA_FEA, 0x03);    
    ide_outp(ATAPI_ATA_SEC, mode_value);
    ide_outp(ATAPI_ATA_DEVH, drive_no ? SLAVE_MODE : MASTER_MODE);       
    ide_outp(ATAPI_ATA_COMD, SET_FEATURE);
   	
   	/* check INTRQ interrupt */
   	if( !atapi_wait_INTRQ())
   	{
   		sysprintf("wait INTRQ error !!\n");	 
	 	return 0;
   	}
   	_atapi_INTRQ = FALSE;     
   	
    /* make sure the output is OK */
    if (!atapi_wait_BSY())	 
	{
    	sysprintf("wait BSY error !!\n");	 
	 	return 0;
	}
		
	
    /* check HD's status */
    param = ide_inp(ATAPI_ATA_STAT);
    if ( (param & 0xE9) != 0x40 )     
    {
    	param = ide_inp(ATAPI_ATA_ERR);
    	sysprintf("[FAIL]ATAPI error register [%x]!!\n", param & 0xff);	 
	 	return 0;
	}   
    
    return 1;

} /* end atapiWriteSector */

//------------------------------------------------------------------------
//  Hardware reset for all IDE device(HD)
//  
//  Input : device : 0=reset device 0, 1=reset devcie 1, 2=reset both device
//  Ret: 0: fail
//       1 : success
//------------------------------------------------------------------------
int atapiHWReset(INT device)
{
    UINT32 i, tick;
    UINT32 status;
	
    outpw(ATAPI_CSR, (inpw(ATAPI_CSR) & (~0x02)) | 0x01); // RESET- pin in high level
                                                                                // bit 0: Engine reset 
    for(i=0;i<1500;i++) ;                                            // delay 25us	
    outpw(ATAPI_CSR, inpw(ATAPI_CSR) | 0x02);      // RESET- pin in low level	 
#ifdef ECOS
	tick = cyg_current_time();
	while((cyg_current_time() - tick) < 2);
#else    
    tick=sysGetTicks(TIMER0);                                   // delay at least 2ms    
    while((sysGetTicks(TIMER0)-tick)<2) ;  // wait at least 10ms due to Timer0 minimum setting
#endif    
    outpw(ATAPI_CSR, inpw(ATAPI_CSR) & (~0x02));  // RESET- pin in high level
 
#ifdef ECOS
	while((cyg_current_time() - tick) < 50);
#else
    // wait device ready (busy=0) (about 500-600 ms)   (06/5/12)    
    while((sysGetTicks(TIMER0)-tick)<50)  ;     // delay 50*10 ms
#endif	
	
	if(device == 0 || device == 2)
	{
		sysprintf("check deivce 0 status ...\n");
		ide_outp(ATAPI_ATA_DEVH, MASTER_MODE);       
		
	    // check BSY=0	
	    for (i=0; i<500000;i++ )   // wait busy             
	    {
	        status = ide_inp(ATAPI_ATA_STAT);
	        status &= 0xc0;
	        if (status == 0x40)     // DRDY=1, BSY=0
	        {
	        	bAtapiMasterIsUsed = 1;
	        	sysprintf("Master found!!\n");
				break;
			}
	    }  	
	}
	
	if(device == 1 || device == 2)
	{
		sysprintf("check deivce 1 status ...\n");	
		ide_outp(ATAPI_ATA_DEVH, SLAVE_MODE);       
		
	    // check BSY=0	
	    for (i=0; i<500000;i++ )   // wait busy
	    {
	        status = ide_inp(ATAPI_ATA_STAT);
	        status &= 0xc0;
	        if (status == 0x40)     // DRDY=1, BSY=0
	        {
	         	bAtapiSlaveIsUsed = 1;
	         	sysprintf("Slave found!!\n");
	         	break;
	        }
	    }  
	 }
	 
	if(device == 2 && !bAtapiSlaveIsUsed)
		atapiHWReset(0);
	else if(device == 2 && !bAtapiMasterIsUsed)
		atapiHWReset(1);
    
    if(!bAtapiMasterIsUsed && !bAtapiSlaveIsUsed)
    	return 0;  // Reset fail
    else
    	return 1;
}
//------------------------------------------------------------------------
//  Wait DMATIP==0, DMA/UDMA transfer is not in progress
//
//  Ret: 0 : DMA busy
//       1 : DMA finished
//------------------------------------------------------------------------
int atapi_wait_DMATIP(void)
{
    UINT32   i;
  
    for (i=0; i<ATAPI_DELAY_LOOP; i++)
  	    if (!(inpw(ATAPI_DMACSR) & ATAPI_DMACSR_DMATIP))	 
  	        return 1;        // DMATIP==0, DMA finished
    return 0;    // busy   in progress
} 
//------------------------------------------------------------------------
//  Read Status Register, wait DRQ=n
//          DRQ=0 : Data Register is ready to send or receive data 
//          DRQ=1 : Data Register is no data 
//
//  In : n (wait DRQ=n, n=0 or 1)
//
//  Ret: 0 : if DRQ!=n
//       1 : if DRQ=n
//------------------------------------------------------------------------
int atapi_wait_DRQ(UCHAR n)
{
    UINT32 status;
    UINT32   i;
  
    for (i=0; i<ATAPI_DELAY_LOOP; i++)
        {
  	     status = ide_inp(ATAPI_ATA_STAT);
  	     status &= 0x08;
            if ((status>>3) == (UINT32)n)
  	              return(1);     // if DRQ=n, return 1
        }
    return(0);    // busy
} 
//------------------------------------------------------------------------
//  Wait INTRQ assert from HD
//
//  Ret: 0 : INTRQ low
//       1 : INTRQ assert
//------------------------------------------------------------------------
int atapi_wait_INTRQ(void)
{
    UINT32   i;

    for (i=0; i<ATAPI_DELAY_LOOP; i++)
        {
            if (_atapi_INTRQ)
		        return 1;		 
        }
    return 0;    // busy	
} 
//------------------------------------------------------------------------
//  Wait ENDOFSECTOR interrupt assert
//
//  Ret: 0 : ENDOFSECTOR low
//       1 : ENDOFSECTOR assert
//------------------------------------------------------------------------
int atapi_wait_ENDOFSECTOR(void)
{
    UINT32   i;

    for (i=0; i<ATAPI_DELAY_LOOP; i++)
        {
            if (_atapi_ENDOFSECTOR)
		        return 1;		 
        }
    return 0;    // busy	
} 

//------------------------------------------------------------------------
//  Read Status Register, wait BSY=0
//
//  Ret: 0 : HD still busy, BSY=1
//       1 : not busy, BSY=0
//------------------------------------------------------------------------
int atapi_wait_BSY(void)
{
    UINT32 status;
    UINT32   i;
  
    for (i=0; i<ATAPI_DELAY_LOOP; i++)
    {
        status = ide_inp(ATAPI_ATA_STAT);
        status &= 0xC0;
        if ( status == 0x40)	 // if BSY=0 & DRDY=1, can accept command
            return 1;    // BSY=0
    }
    
    return 0;    // busy
} 
//------------------------------------------------------------------------
//  Initial atapi engine, and check HD if exist ?
//
//  In : engine_clock  0: 33MHz
//                     1: 66MHz
//       fScatterGather : 0 : not use scatter-gather
//                        1 : use it
//
//  Out: IDE_HEADER_INFO[2] : master/slave HD information
//
//  Ret: number of existing HD
//------------------------------------------------------------------------
//int atapiInitial(UCHAR engine_clock, UCHAR fScatterGather)
int atapiInitial(UCHAR engine_clock)
{  
   	INT nTotalHDD = 0;
   	
   	outpw(0xb0000200, inpw(0xb0000200) | 0x40); //enable engine clock
   
#ifdef ECOS
	cyg_drv_interrupt_create(IRQ_ATAPI, 1, 0, atapiISR, NULL,
								&atapi_int_handle, &atapi_int);
    cyg_drv_interrupt_attach(atapi_int_handle);
    cyg_drv_interrupt_unmask(IRQ_ATAPI);
#else   
    // Setup own interrupt service routine to AIC interrupt vector table 
    sysInstallISR(IRQ_LEVEL_1, IRQ_ATAPI, (PVOID)atapiISR);    
    // Enable CPSR I bit 
    sysSetLocalInterrupt(ENABLE_IRQ);	    
    // Enable the interrupt for JPEG engine 
    sysEnableInterrupt(IRQ_ATAPI);
#endif

    if (engine_clock)     //66MHz
         outpw(ATAPI_CSR, ATAPI_CSR_ATA_EN | 0x08);  // engine code logic by 66M  
    else                         //33MHz
         outpw(ATAPI_CSR, ATAPI_CSR_ATA_EN);  // engine code logic by 33M    

    outpw(ATAPI_INTR, ATAPI_INTR_DTA_IF | ATAPI_INTR_EOS_IF|
                      ATAPI_INTR_INTRQ_IF |
                      ATAPI_INTR_DTA_IE | ATAPI_INTR_EOS_IE | 
                      ATAPI_INTR_INTRQ_IE);  
                     
    _atapi_DMACRWABORT = _atapi_ENDOFSECTOR = 
    _atapi_DMARQ = _atapi_INTRQ = FALSE;	
	
    outpw(0xb000000c, (inpw(0xb000000c)& 0xf3ffffff) | 0x04000000);  // GPIO select : Atapi 

	// set transfer timing
    atapiSetTransferTiming(0, 4, engine_clock);  // REG timing : mode 4
    atapiSetTransferTiming(1, 4, engine_clock);  // set PIO timing    
                                                 // set PIO mode 4 for Drive Identify command
                                   
    //gScatterGather=fScatterGather;    // set flag to decide if use Scatter Gather DMA
    
    // reset both master and slave
    if (!atapiHWReset(2))
    {
  	    sysprintf("HW reset fail \n");
  	    return 0;     // reset failed
  	}
    
    if(bAtapiMasterIsUsed)
    {
	    sysprintf("Read Master HD Info...\n");    
	    if(atapiReadHDInfo(0))
	    	nTotalHDD ++;
	}   		
	
	if(bAtapiSlaveIsUsed)
	{
	    sysprintf("Read Slave HD Info...\n");
	    if(atapiReadHDInfo(1))
	    	nTotalHDD ++;
    }
    

	sysprintf("Found %d HDD ...\n", nTotalHDD);
	
	return nTotalHDD;
} 

//------------------------------------------------------------------------
//  Set transfer mode : PIO : set multiple command (only support master HD)
//                    DMA/UDMA : 1. set timing
//                               2. set feature on master/slave HD)
//
//  In : engine_clock : 0: 33MHz   1: 66MHz
//                      2: set timing from AP level(used for testing 15MHz clock)
//
//  Out: gDMAtype : 0: DMA mode
//                  1: UDMA mode 
//
//  Ret: 0: fail
//       1: success
//------------------------------------------------------------------------
int atapiInitTransferMode(USHORT engine_clock)
{    
    USHORT udma_mode0, udma_mode1;
    USHORT transfer_type0, transfer_type1;
    USHORT mode0, mode1;
    
    //decide Master/Slave HDD transfer mode
    udma_mode0=IDE_HEADER_INFO[0].udma_modes & 0x3f;
    udma_mode1=IDE_HEADER_INFO[1].udma_modes & 0x3f;
    
    //default is UDMA mode
    if(bAtapiMasterIsUsed)
    {
	    // decide master mode
	    if (udma_mode0==0x3f)	
		{
			transfer_type0 = 2;
			mode0 = 4;
		}
	    else if (udma_mode0==0x1f)	
		{
			transfer_type0 = 2;
			mode0 = 4;
		}
	    else if (udma_mode0==0x0f)	
		{
			transfer_type0 = 2;
			mode0 = 3;
		}
	    else if (udma_mode0==0x07)	
		{
			transfer_type0 = 2;
			mode0 = 2;
		}
	    else if (udma_mode0==0x03)	
		{
			transfer_type0 = 2;
			mode0 = 1;
		}
	    else if (udma_mode0==0x01)	
		{
			transfer_type0 = 2;
			mode0 = 0;
		}
	    else if (udma_mode0==0)		
		{  
			transfer_type0 = 0;		//assume PIO Mode4
			mode0 = 4;
			sysprintf("Master doesn't support Ultra DMA\n");
		}    
	}
    
    if(bAtapiSlaveIsUsed)
    {
	    //decide slave mode
	    if (udma_mode1==0x3f)	
		{
			transfer_type1 = 2;
			mode1 = 4;
		}
	    else if (udma_mode1==0x1f)	
		{
			transfer_type1 = 2;
			mode1 = 4;
		}
	    else if (udma_mode1==0x0f)	
		{
			transfer_type1 = 2;
			mode1 = 3;
		}
	    else if (udma_mode1==0x07)	
		{
			transfer_type1 = 2;
			mode1 = 2;
		}
	    else if (udma_mode1==0x03)	
		{
			transfer_type1 = 2;
			mode1 = 1;
		}
	    else if (udma_mode1==0x01)	
		{
			transfer_type1 = 2;
			mode1 = 0;
		}
	    else if (udma_mode1==0)		
		{  
			transfer_type1 = 0;		//assume PIO Mode4
			mode1 = 4;
			sysprintf("Slave doesn't support Ultra DMA\n");
		}    
	}
	
	if(bAtapiMasterIsUsed)
		sysprintf("Master : %s-mode %d is selected\n",transfer_type0 == 2 ? "UDMA":"PIO", mode0);
	
	if(bAtapiSlaveIsUsed)
		sysprintf("Slave : %s-mode %d is selected\n",transfer_type1 == 2 ? "UDMA":"PIO", mode1);
			
    if (transfer_type0==0)   // PIO mode
	{
        if (!atapiSetFeature(0,transfer_type0, mode0))
        {
	   		sysprintf("atapiSetFeature() error...\n");
	   		return 0;	
	   	}
     	
     	// only support master HD on PIO mode	
        if (!atapiSetMultiple(0, IDE_HEADER_INFO[0].support_max_sector_no & 0x00ff))
        {
	   		sysprintf("atapiSetMultiple() error...\n");
	   		return 0;
	   	}
	}
    else if (transfer_type0==1 || transfer_type1==1)    // DMA mode
	{
	    gDMAtype=0;          	 
	    if (engine_clock != 2)
             atapiSetTransferTiming(2, mode0, engine_clock);  // DMA timing
        if(bAtapiMasterIsUsed)
	        if (!atapiSetFeature(0, transfer_type0, mode0))      // set master HD
		  		 return 0;
		if(bAtapiSlaveIsUsed)
	        if (!atapiSetFeature(1, transfer_type1, mode1))      // set slave HD
		   		return 0;
	 }
    else if (transfer_type0==2 || transfer_type1==2)
    {
	     gDMAtype=1;      			        
	    if (engine_clock != 2)
             atapiSetTransferTiming(3, mode0, engine_clock);  // UDMA timing
        if(bAtapiMasterIsUsed)
	        if (!atapiSetFeature(0, transfer_type0, mode0))      // set master HD
		   		return 0;
		if(bAtapiSlaveIsUsed)
	        if (!atapiSetFeature(1, transfer_type1, mode1))      // set slave HD
		   		return 0;

	}	    	 	

    return 1;    	 
} 
//------------------------------------------------------------------------
//  Get the max sector number of selected device. (unit : sector)
//
//  In : drive_no : selected HD
//
//  Ret: total sector number
//------------------------------------------------------------------------
UINT32 atapiGetDriveCapacity(USHORT drive_no)
{
    if ((IDE_HEADER_INFO[drive_no].command_set_support & 0x0400))  
       {
          // support 48-bits sector address   max. 2200G
          return (IDE_HEADER_INFO[drive_no].max_lba_for_48bit)[0];
       }    
    else
       {
          // support 28-bits sector address   max. 137G
          return *((UINT32 *)(IDE_HEADER_INFO[drive_no].lba_capacity));   
       }
}

